<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Controls II</title>
<link rel="stylesheet" href="/cfg/format.css" type="text/css">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="keywords" content="Windows API, Winapi, Windows Controls II, 
Trackbar, Month Calendar Control">
<meta name="description" content="In this chapter of the Windows API tutorial,
we continue working with Windows controls.">
<meta name="language" content="en">
<meta name="author" content="Jan Bodnar">
<meta name="distribution" content="global">

<script type="text/javascript" src="/lib/jquery.js"></script>
<script type="text/javascript" src="/lib/common.js"></script>

</head>

<body>

<div class="container2">

<div id="wide_ad" class="ltow">
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* 160x600, August 2011 */
google_ad_slot = "2484182563";
google_ad_width = 160;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>

<div class="content2">


<a href="/" title="Home">Home</a>&nbsp;
<a href="..">Contents</a>


<h1>Controls II.</h1>


<p>
We continue with Windows Controls. We will show how to use a Trackbar, 
a Tooltip and a Month calendar control. 
</p>

<div class="big_hor">
<script type="text/javascript"><!--
google_ad_client = "ca-pub-9706709751191532";
/* big_horizontal */
google_ad_slot = "2904953388";
google_ad_width = 728;
google_ad_height = 90;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>


<h2>Trackbar</h2>

<p> 
A trackbar is a window that contains a slider and optional tick marks. We move 
the slider using the mouse or keyboard. A trackbar is used to select discrete 
values from a range of consecutive values. This control is called a slider on 
other platforms. 
</p>

<pre class="code">
#include &lt;windows.h&gt;
#include &lt;commctrl.h&gt;

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void CreateControls(HWND hwnd);
void UpdateLabel(void);

HWND hTrack;
HWND hlbl;

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
  PWSTR lpCmdLine, int nCmdShow)
{
  HWND hwnd;
  MSG  msg ;

  WNDCLASSW wc = {0};
  wc.lpszClassName = L"Trackbar";
  wc.hInstance     = hInstance ;
  wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
  wc.lpfnWndProc   = WndProc ;
  wc.hCursor       = LoadCursor(0,IDC_ARROW);
  
  RegisterClassW(&amp;wc);
  hwnd = CreateWindowW(wc.lpszClassName, L"Trackbar", 
    WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 350, 180, 0, 0, hInstance, 0);
  
  while( GetMessage(&amp;msg, NULL, 0, 0)) {
    TranslateMessage(&amp;msg);
    DispatchMessage(&amp;msg);
  }

  return (int) msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, 
  WPARAM wParam, LPARAM lParam )
{
  switch(msg)  
  {
     case WM_CREATE:
       CreateControls(hwnd);
       break;

     case WM_HSCROLL:
       UpdateLabel();
       break;

     case WM_DESTROY:
       PostQuitMessage(0);
       break; 
  }

  return DefWindowProcW(hwnd, msg, wParam, lParam);
}

void CreateControls(HWND hwnd)
{
  HWND hLeftLabel = CreateWindowW(L"STATIC", L"0", 
    WS_CHILD | WS_VISIBLE, 0, 0, 10, 30, hwnd, (HMENU)1, NULL, NULL);

  HWND hRightLabel = CreateWindowW(L"STATIC", L"100", 
    WS_CHILD | WS_VISIBLE, 0, 0, 30, 30, hwnd, (HMENU)2, NULL, NULL);

  hlbl = CreateWindowW(L"STATIC", L"0", WS_CHILD | WS_VISIBLE, 
    270, 20, 30, 30, hwnd, (HMENU)3, NULL, NULL);

  INITCOMMONCONTROLSEX icex;

  icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
  icex.dwICC  = ICC_LISTVIEW_CLASSES;
  InitCommonControlsEx(&amp;icex); 

  hTrack = CreateWindowW(L"msctls_trackbar32", L"Trackbar Control",
      WS_CHILD | WS_VISIBLE | TBS_AUTOTICKS,
      20, 20, 170, 30, hwnd, (HMENU) 3, NULL, NULL);

  SendMessageW(hTrack, TBM_SETRANGE,  TRUE, MAKELONG(0, 100)); 
  SendMessageW(hTrack, TBM_SETPAGESIZE, 0,  10); 
  SendMessageW(hTrack, TBM_SETTICFREQ, 10, 0); 
  SendMessageW(hTrack, TBM_SETPOS, FALSE, 0); 
  SendMessageW(hTrack, TBM_SETBUDDY, TRUE, (LPARAM) hLeftLabel); 
  SendMessageW(hTrack, TBM_SETBUDDY, FALSE, (LPARAM) hRightLabel); 
}

void UpdateLabel(void)
{
  LRESULT pos = SendMessageW(hTrack, TBM_GETPOS, 0, 0);
  wchar_t buf[4];
  wsprintfW(buf, L"%ld", pos);

  SetWindowTextW(hlbl, buf);
}
</pre>

<p>
In our examle we dislay a Trackbar control with three static text controls. 
Two of them are attached to the left and to the right of the trackbar. They are 
called buddies. By dragging the slider, we change the text of the third static
control.
</p>

<pre class="explanation">
HWND hLeftLabel = CreateWindowW(L"STATIC", L"0", 
  WS_CHILD | WS_VISIBLE, 0, 0, 10, 30, hwnd, (HMENU)1, NULL, NULL);

HWND hRightLabel = CreateWindowW(L"STATIC", L"100", 
  WS_CHILD | WS_VISIBLE, 0, 0, 30, 30, hwnd, (HMENU)2, NULL, NULL);

hlbl = CreateWindowW(L"STATIC", L"0", WS_CHILD | WS_VISIBLE, 
  270, 20, 30, 30, hwnd, (HMENU)3, NULL, NULL);
</pre>

<p>
Three static controls are created. Two controls will display the minimum and
maximum value of the Trackbar control. The last one will display the currently
selected value.
</p>

<pre class="explanation">
INITCOMMONCONTROLSEX icex;

icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC  = ICC_LISTVIEW_CLASSES;
InitCommonControlsEx(&amp;icex); 
</pre>

<p>
If we want to use one of the common controls, we need to load the common 
control DLL (comctl32.dll) and register specific common control classes from 
the DLL. The <code>InitCommonControlsEx()</code> must call this function before 
creating a common control.
</p>

<pre class="explanation">
hTrack = CreateWindowW(L"msctls_trackbar32", L"Trackbar Control",
    WS_CHILD | WS_VISIBLE | TBS_AUTOTICKS,
    20, 20, 170, 30, hwnd, (HMENU) 3, NULL, NULL);
</pre>

<p>
A Trackbar common control is created. The <code>TBS_AUTOTICKS</code> style 
creates a tick mark for each increment in its range of values.
</p>

<pre class="explanation">
SendMessageW(hTrack, TBM_SETRANGE,  TRUE, MAKELONG(0, 100)); 
SendMessageW(hTrack, TBM_SETPAGESIZE, 0,  10); 
SendMessageW(hTrack, TBM_SETTICFREQ, 10, 0); 
SendMessageW(hTrack, TBM_SETPOS, FALSE, 0); 
</pre>

<p>
We are not yet finished with the Trackbar control. We send four messages to the
control. We send a <code>TBM_SETRANGE</code> to set the trackbar range. To set 
the page size, we send the <code>TBM_SETPAGESIZE</code> message. To set the tick 
frequency, we send the <code>TBM_SETTICFREQ</code> message. To set the current 
slider position we send the <code>TBM_SETPOS</code>.
</p>


<pre class="explanation">
SendMessageW(hTrack, TBM_SETBUDDY, TRUE, (LPARAM) hLeftLabel); 
SendMessageW(hTrack, TBM_SETBUDDY, FALSE, (LPARAM) hRightLabel);  
</pre>

<p>
We set the Trackbar buddies by sending the <code>TBM_SETBUDDY</code> message.
The third parameter will decide, whethe the buddy is located to the left (TRUE) 
or to the right (FALSE) of the control. 
</p>

<pre class="explanation">
case WM_HSCROLL:
  UpdateLabel();
  break;
</pre>

<p>
When we move the Trackbar slider, the window procedure receives the 
<code>WM_HSCROLL</code> message. (In case of a horizontal trackbar.)
</p>

<pre class="explanation">
void UpdateLabel(void)
{
  LRESULT pos = SendMessageW(hTrack, TBM_GETPOS, 0, 0);
  wchar_t buf[4];
  wsprintfW(buf, L"%ld", pos);

  SetWindowTextW(hlbl, buf);
}
</pre>

<p>
In the UpdateLabel() method, we we get the current slider position by sending 
the <code>TMB_GETPOS</code> message. The received value is converted to text
using the <code>wsprintfW()</code> function. Finally, the text of the static
control is set with the <code>SetWindowTextW()</code> function. 
</p>


<img src="/img/gui/winapi/trackbar.png" alt="Trackbar">
<div class="figure">Figure: Trackbar</div>


<h2>A tooltip</h2>

<p>
A tooltip is a common graphical user element. Tooltip is hidden most of the time. 
It is a small box that appears near an GUI object when a mouse pointer passes over it. 
It displays a brief message explaining the object. Tooltips are part of the 
help system of an application. 	
</p>

<pre class="code">
#include &lt;windows.h&gt;
#include &lt;commctrl.h&gt;

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void CreateMyTooltip(HWND);


int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
			LPSTR lpCmdLine, int nCmdShow )
{
  MSG  msg ;    
  WNDCLASS wc = {0};
  wc.lpszClassName = "Tooltip" ;
  wc.hInstance     = hInstance ;
  wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
  wc.lpfnWndProc   = WndProc ;
  wc.hCursor       = LoadCursor(0, IDC_ARROW);
  
  RegisterClass(&amp;wc);
  CreateWindow( wc.lpszClassName, "Tooltip",
                WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                100, 100, 200, 150, 0, 0, hInstance, 0);  

  while( GetMessage(&amp;msg, NULL, 0, 0)) {
    TranslateMessage(&amp;msg);
    DispatchMessage(&amp;msg);
  }
  return (int) msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
  switch(msg)  
  {
    case WM_CREATE:
        CreateMyTooltip(hwnd);
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}

void CreateMyTooltip (HWND hwnd)
{

    INITCOMMONCONTROLSEX iccex; 
    HWND hwndTT;                

    TOOLINFO ti;
    char tooltip[30] = "A main window";
    RECT rect;                 
  
    iccex.dwICC = ICC_WIN95_CLASSES;
    iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
    InitCommonControlsEx(&amp;iccex);

    hwndTT = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL,
        WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,		
        0, 0, 0, 0, hwnd, NULL, NULL, NULL );

    SetWindowPos(hwndTT, HWND_TOPMOST, 0, 0, 0, 0,
        SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
   
    GetClientRect (hwnd, &amp;rect);

    ti.cbSize = sizeof(TOOLINFO);
    ti.uFlags = TTF_SUBCLASS;
    ti.hwnd = hwnd;
    ti.hinst = NULL;
    ti.uId = 0;
    ti.lpszText = tooltip;
    ti.rect.left = rect.left;    
    ti.rect.top = rect.top;
    ti.rect.right = rect.right;
    ti.rect.bottom = rect.bottom;

    SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &amp;ti);	
} 		
</pre>

<p>
In our example, we set a tooltip for the main window.
</p>

<pre class="explanation">
 INITCOMMONCONTROLSEX iccex;
 ...
 iccex.dwICC = ICC_WIN95_CLASSES;
 iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
 InitCommonControlsEx(&amp;iccex);
</pre>

<p>
A tooltip is a part of common controls. We must initialize common controls. 
</p>

<p>
Creation of a tooltip consists of several steps. We must create a tooltip window. 
Then we make it a topmost window, so that it is not covered by another window.
We create a tooltip text and <i>TOOLTIPINFO</i> structure. The structure must be 
filled with important info. The window handle, tooltip text and the rectangle, which 
will our tooltip cover. In our example, our tooltip will cover the whole client area of a window. 
<p>


<pre class="explanation">
 SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &amp;ti);
</pre>

<p>
The tooltip is really added to the window, after we send the <i>TTM_ADDTOOL</i> message.
</p>

<br>
<img src="/img/gui/winapi/tooltip.jpg" alt="Tooltip">



<h2>Month Calendar Control</h2>

<p>
A Month Calendar is a complex control which is used to select a a date. We can 
select a date in a simple and 
intuitive way.
</p>

<pre class="code">
#include &lt;windows.h&gt;
#include &lt;commctrl.h&gt;
#include &lt;wchar.h&gt;

#define DATE_SIZE 20

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void CreateControls(HWND);
void GetSelectedDate(HWND, HWND);

HWND hlbl;
HWND hMonthCal;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
  LPSTR lpCmdLine, int nCmdShow)
{
  HWND hwnd;
  MSG  msg;
    
  WNDCLASSW wc = {0};
  wc.lpszClassName = L"Month Calendar";
  wc.hInstance     = hInstance ;
  wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
  wc.lpfnWndProc   = WndProc ;
  wc.hCursor       = LoadCursor(0,IDC_ARROW);
  
  RegisterClassW(&amp;wc);

  hwnd = CreateWindowW(wc.lpszClassName, L"Month Calendar",
    WS_OVERLAPPEDWINDOW | WS_VISIBLE,
    100, 100, 250, 300, 0, 0, hInstance, 0);  

  while( GetMessage(&amp;msg, NULL, 0, 0)) {
    DispatchMessage(&amp;msg);
  }

  return (int) msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, 
  WPARAM wParam, LPARAM lParam )
{
  LPNMHDR lpNmHdr;

  switch(msg)  
  {
    case WM_CREATE:

      CreateControls(hwnd);
      break;

    case WM_NOTIFY:

      lpNmHdr = (LPNMHDR) lParam;

      if (lpNmHdr->code==MCN_SELECT) {
        GetSelectedDate(hMonthCal, hlbl);
      }
      break;

    case WM_DESTROY:

      PostQuitMessage(0);
      break; 
  }

  return DefWindowProcW(hwnd, msg, wParam, lParam);
}

void CreateControls(HWND hwnd)
{
  hlbl = CreateWindowW(L"STATIC", L"", 
    WS_CHILD | WS_VISIBLE, 80, 240, 80, 30,
    hwnd, (HMENU)1, NULL, NULL);

  INITCOMMONCONTROLSEX icex;

  icex.dwSize = sizeof(icex);
  icex.dwICC  = ICC_DATE_CLASSES;
  InitCommonControlsEx(&amp;icex);

  hMonthCal = CreateWindowW(L"SysMonthCal32", L"",          
    WS_BORDER | WS_CHILD | WS_VISIBLE | MCS_NOTODAYCIRCLE,  
    20, 20, 200, 200, hwnd, (HMENU)2, NULL, NULL);
}

void GetSelectedDate(HWND hMonthCal, HWND hlbl)
{
  SYSTEMTIME time;
  wchar_t date[DATE_SIZE];

  ZeroMemory(&amp;time, sizeof(SYSTEMTIME));
  SendMessage(hMonthCal, MCM_GETCURSEL, 0, (LPARAM) &amp;time);
  
  swprintf(date, DATE_SIZE, L"%d-%d-%d", time.wYear, time.wMonth, time.wDay);
  SetWindowTextW(hlbl, date);
}
</pre>

<p>
In our example, we have two controls. A month calendar control and a static text. 
The selected date from the month calendar control is dispalyed in the static text.
</p>

<pre class="explanation">
hMonthCal = CreateWindowW(L"SysMonthCal32", L"",          
  WS_BORDER | WS_CHILD | WS_VISIBLE | MCS_NOTODAYCIRCLE,  
  20, 20, 200, 200, hwnd, (HMENU)2, NULL, NULL);
</pre>

<p>
Here we create a month calendar control. The class name to create a month 
calender control is <code>L"SysMonthCal32"</code>. If we use the  
<code>MCS_NOTODAYCIRCLE</code> window style, the today's date is not circled.
</p>

<pre class="explanation">
INITCOMMONCONTROLSEX icex;

icex.dwSize = sizeof(icex);
icex.dwICC  = ICC_DATE_CLASSES;
InitCommonControlsEx(&amp;icex);
</pre>

<p>
To register a month calendar control, we specify the 
<code>ICC_DATE_CLASSES</code> flag for the dwICC member of the 
<code>INITCOMMONCONTROLSEX</code> structure.
</p>

<pre class="explanation">
case WM_NOTIFY:

  lpNmHdr = (LPNMHDR) lParam;

  if (lpNmHdr->code==MCN_SELECT) {
    GetSelectedDate(hMonthCal, hlbl);
  }
  break;
</pre>

<p>
If an event occurs in the month calendar control, the <code>WM_NOTIFY</code> 
message is sent. The <code>lParam</code> contains a pointer to an 
<code>NMHDR</code> structure that contains the notification code and additional 
information.
</p>

<pre class="explanation">
SendMessage(hMonthCal, MCM_GETCURSEL, 0, (LPARAM) &amp;time);
</pre>

<p>
To fill the structure with the selected date, we send a 
<code>MCM_GETCURSEL</code> message to the calendar control.
</p>

<pre class="explanation">
swprintf(date, DATE_SIZE, L"%d-%d-%d", time.wYear,
  time.wMonth, time.wDay);
SetWindowTextW(hlbl, date);
</pre>

<p>
We retrieve the data and set the date to the static text control.
</p>

<img src="/img/gui/winapi/monthcal.png" alt="Month Calendar">
<div class="figure">Figure: Month Calendar</div>


<p>
In this part of the Windows API tutorial, we have continued covering 
controls. 
</p>


<div class="center"> 
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* horizontal */
google_ad_slot = "1734478269";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 
</div> 
<br> 

<div class="botNav, center">
<span class="botNavItem"><a href="/">Home</a></span> ‡ <span class="botNavItem"><a href="..">Contents</a></span> ‡
<span class="botNavItem"><a href="#">Top of Page</a></span>
</div>


<div class="footer">
<div class="signature">
<a href="/">ZetCode</a> last modified October 8, 2012  <span class="copyright">&copy; 2007 - 2013 Jan Bodnar</span>
</div>
</div>

</div> <!-- content -->

</div> <!-- container -->

</body>
</html>

